home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
038a
/
basprint.zip
/
BASPRINT.ASM
Wrap
Assembly Source File
|
1991-06-19
|
19KB
|
809 lines
COMMENT *
CLUBware (tm)
BASPRINT - Enhancement to the Basic Interpreter Print statement
Copyright 1984 Rayhawk Automation N.W. Inc
P.O. Box 1427
Beaverton, Oregon 97075
*
SCRDSEG SEGMENT
ASSUME CS:SCRDSEG,DS:NOTHING,ES:NOTHING,SS:STACK
ORIG_INT10 LABEL WORD ; original int 10
DW 0
DW 0
;______________________________________________________________________________
; Swap the B4 interrupt used by BASIC to print strings. Swap must be made
; after BASIC has been loaded and initialized its environment. Swap takes
; place when BASIC clears the screen.
ORIG_INTB4 LABEL WORD ; original int b4
DW 0
DW 0
INTSWAP PROC FAR
CMP AX,0600h ; clear screen request?
JE DO_SWAP
JMP DWORD PTR ORIG_INT10 ; pass call to BIOS
DO_SWAP:
PUSH DS
PUSH AX
SUB AX,AX ; address low memory
MOV DS,AX
CLI ; disable interrupts for a moment
; vector for B4 is at 2D0
; have we already taken over this interrupt once before?
CMP WORD PTR DS:[02D0h],OFFSET PRINTER
JE ALREADY_SAVED
; save the original interrupt vector for use by error message reports
MOV AX,WORD PTR DS:[02D0h]
MOV ORIG_INTB4,AX
MOV AX,WORD PTR DS:[02D2h]
MOV ORIG_INTB4+2,AX
; replace the original interrupt vector to allow us to intercept prints
MOV AX,OFFSET PRINTER
MOV WORD PTR DS:[02D0h],AX
MOV AX,SEG PRINTER
MOV WORD PTR DS:[02D2h],AX
ALREADY_SAVED:
STI
POP AX
POP DS
JMP DWORD PTR ORIG_INT10 ; pass clear screen to BIOS now
INTSWAP ENDP
;______________________________________________________________________________
COMMENT * PRINTER prints a character string on the screen starting
at the current cursor position. After the string is
written to the screen the cursor position is updated
to just after the string.
To use this routine, INTSWAP must have swapped the interrupt
vector for intterupt B4 and redirected it to the PRINTER
subroutine.
The BASPRINT module will take over the INT 10h and make
this module permanently resident. When the screen is cleared
by BASIC, INTSWAP will swap INT B4 and point the vector
to the PRINTER subroutine.
Algorithm:
check context for simple text to screen or possibly something
more involved that basic should handle
1) store character count and mark the flag for type of i/o
2) load current page from DS:[0049]
3) load current position on page from DS:[0056]
4) load attribute for string from DS:[004E]
5) move character count to CX for use in loop
6) load type of crt display from 0000:463
7) address the screen segment
8) move string to screen while synchronizing
with horizontal retrace
9) update cursor position on screen
10) update cursor position in database at DS:[0056]
11) leave registers in manner BASIC expects
On entry
DS:[SI] points to string to write
DH contains count of characters to write
DS:[CX] also points to string and must be updated on exit
DS:[0049] contains the page to which we write
DS:[004E] contains the attribute for the text
DS:[0056] contains the current screen location and must
be updated after print is complete
*
;______________________________________________________________________________
; Local data addressable through CS register
CHAR_COUNT LABEL BYTE
DB 0
LINE_START LABEL WORD
DW 0 ; 0
DW 160 ; 1
DW 320 ; 2
DW 480 ; 3
DW 640 ; 4
DW 800 ; 5
DW 960 ; 6
DW 1120 ; 7
DW 1280 ; 8
DW 1440 ; 9
DW 1600 ; 10
DW 1760 ; 11
DW 1920 ; 12
DW 2080 ; 13
DW 2240 ; 14
DW 2400 ; 15
DW 2560 ; 16
DW 2720 ; 17
DW 2880 ; 18
DW 3040 ; 19
DW 3200 ; 20
DW 3360 ; 21
DW 3520 ; 22
DW 3680 ; 23
DW 3840 ; 24
PAGE_START LABEL WORD
DW 0
DW 4000
DW 8000
DW 12000
STARTING_PAGE LABEL WORD
DW 0
END_OF_PAGE LABEL WORD
DW 0
STARTING_LINE LABEL WORD
DW 0
RESIDUAL LABEL BYTE
DB 0
ROM_SEGMENT LABEL WORD ; basic segment in ROM
DW 0F600h
LOCAL_FLAG LABEL BYTE
DB 00000000b
JUST_BLANKS EQU 00000001b ; basic wants just blanks displayed
;______________________________________________________________________________
COMMENT *
New service routine for interrupt B4
Calls to routine F600:2BA5 generate B4 interrupts.
These calls come from several places within the BASIC ROM.
We service calls from two locations: 26CC - call to display a string
148D - call to display blanks
Calls from anywhere else are serviced by the original basic code.
The two labels corresponding to the two types of calls we service
are STRING_IO
REPEAT_BLANKS
The code labeled BASIC_IO passes the interrupt back to the basic code
for service.
*
PRINTER PROC FAR
PUSH BP
MOV BP,SP ; address the stack
; check context for simple text to screen or possibly something
; more involved that basic should handle
CMP WORD PTR DS:[04E9h],0
JNE BASIC_IO
CMP WORD PTR DS:[071Fh],0
JNE BASIC_IO
CMP BYTE PTR DS:[0758h],0
JNE BASIC_IO
CMP BYTE PTR DS:[071Bh],0
JNE BASIC_IO
CMP BYTE PTR DS:[0029h],50h ; 80 bytes per line?
JNE BASIC_IO
CMP BYTE PTR DS:[0056h],25 ; on bottom line of screen?
JE BASIC_IO ; if so, let basic handle it
CMP WORD PTR [BP+8],26CCh ; just regular i/o?
JE STRING_IO
CMP WORD PTR [BP+8],148Dh ; just repeated blanks?
JE REPEAT_BLANKS
; allow original B4 service routine to perform I/O
BASIC_IO:
POP BP
JMP DWORD PTR ORIG_INTB4 ; pass control to original B4
; ----------
; repeated blanks sent to screen
REPEAT_BLANKS:
PUSH BX ; save registers used
PUSH DX
PUSH DI
PUSH ES
PUSH CX
; ... 1) store character count and mark the flag for type of i/o
MOV CHAR_COUNT,CH ; save character count
OR LOCAL_FLAG,JUST_BLANKS
MOV RESIDUAL,DH ; dh should be untouched
JMP SETUP_START
; ----------
; write the string for basic
STRING_IO:
PUSH BX ; save registers used,
PUSH DX
PUSH DI
PUSH ES
; ... 1) backup to start of string and store character count
DEC SI ; backup to start of string
MOV CHAR_COUNT,DH ; save character count
MOV LOCAL_FLAG,0 ; clear the flag for string i/o
MOV RESIDUAL,1 ; dh to contain 1 on exit
; ... 2) load current page from DS:[0049]
SETUP_START:
MOV BL,BYTE PTR DS:[0049h] ; load current page
SUB BH,BH
SHL BX,1 ; convert to a table offset
MOV DI,PAGE_START[BX] ; load start of page
MOV STARTING_PAGE,DI ; save the page
MOV END_OF_PAGE,DI ; save ending page pointer
ADD END_OF_PAGE,3840
; ... 3) load current position on page from DS:[0056]
MOV DX,WORD PTR DS:[0056h] ; load current position
DEC DH ; basic counts from 1 instead of 0
DEC DL ; basic counts from 1 instead of 0
MOV BL,DL ; load row number
SHL BX,1 ; two bytes per table entry
ADD DI,LINE_START[BX] ; add in start of line
MOV STARTING_LINE,DI ; store this for later
MOV DL,DH ; bring down column number
SUB DH,DH
ADD DI,DX ; add in column position
ADD DI,DX ; account for attribute bytes
; ... 4) load attribute for string from DS:[004E]
MOV BH,BYTE PTR DS:[004Eh] ; load attribute for string
; ... 5) move character count to CX for use in loop
SUB CH,CH ; clear upper byte
MOV CL,CHAR_COUNT ; load the character count
; ... 6) load type of crt display from 0000:463
SUB AX,AX ; address system memory
MOV ES,AX
MOV DX,WORD PTR ES:[463h] ; load address of display adapter
ADD DX,6 ; address crt status port
; ... 7) address the screen segment
MOV AX,0B000h ; screen seg for monochrome card
CMP DX,03DAh ; is this a graphic card?
JNE MONOCHROME
MOV AX,0B800h ; load screen seg for graphic card
MONOCHROME:
MOV ES,AX ; address the screen buffer
; ... 8) move string to screen while synchronizing
; with horizontal retrace
TEST LOCAL_FLAG,JUST_BLANKS
JZ DISPLAY_LOOP
MOV BL,20h ; load a blank
BLANK_LOOP:
CALL DISPLAY_CHAR ; display a line of blanks for basic
LOOP BLANK_LOOP
JMP UPDATE_POSITION
DISPLAY_LOOP:
LODSB ; load next character
CMP AL,20h ; special character?
JGE NOT_SPECIAL
CALL SPECIAL
JZ ANOTHER_CHAR ; if flag set, go for another character
NOT_SPECIAL:
MOV BL,AL
CLI
HSYNC_WAIT1:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JNZ HSYNC_WAIT1 ; wait for retrace
HSYNC_WAIT2:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JZ HSYNC_WAIT2 ; wait for retrace
MOV AX,BX
STOSW ; store character and attribute
ANOTHER_CHAR:
STI
CMP DI,END_OF_PAGE
JL NOT_SCROLLED
MOV AL,0Dh ; force a carriage return
CALL SPECIAL
NOT_SCROLLED:
LOOP DISPLAY_LOOP ; repeat cx times
; ... 9) update cursor position on screen
UPDATE_POSITION:
MOV AX,DI
SUB AX,STARTING_PAGE
SHR AX,1 ; eliminate attribute bytes
SUB DX,DX
MOV BX,80 ; divide by bytes per line
DIV BX ; quotient in AL (ROW)
; remainder in DL (COLUMN)
MOV DH,AL
MOV BH,BYTE PTR DS:[0049h] ; load page number
MOV AH,2 ; request new position
INT 10h
; ... 10) update cursor position in database at DS:[0056]
XCHG DH,DL ; basic likes this reversed
INC DH ; basic counts from 1 instead of 0
INC DL ; basic counts from 1 instead of 0
MOV WORD PTR DS:[0056h],DX
; ... 11) leave registers in manner BASIC expects
TEST LOCAL_FLAG,JUST_BLANKS
JZ STRING_DISPLAYED
POP CX ; restore original blank count
MOV CH,1 ; allow basic to decrement this to 0
JMP RESTORE_REGS
STRING_DISPLAYED: ; for a string
MOV CX,SI ; CX must point to last character
DEC CX
RESTORE_REGS:
POP ES
POP DI
POP DX
MOV DH,RESIDUAL ; let decrement instr take this to zero
; inside basic interpreter
POP BX
POP BP
ADD SP,4 ; throw away offset and code segment
; from INT B4
POPF ; restore flags from interrupt
POP AX ; throw away near call on stack
PUSH ROM_SEGMENT ; and convert to a far call
PUSH AX
MOV AL,20h ; leave a character in AL for basic
; to compare against a CR (0Dh)
RET ; return to ROM
PRINTER ENDP
;______________________________________________________________________________
; subroutine to handle special control characters
SPECIAL PROC NEAR
; ----------
CMP AL,0Ah ; line feed?
JE NEW_LINE
; ----------
CMP AL,0Bh ; home?
JNE NOT_HOME
MOV DI,STARTING_PAGE ; start over at top of screen
MOV STARTING_LINE,DI
SUB AL,AL
RET
NOT_HOME:
; ----------
CMP AL,0Ch ; clear screen?
JNE NOT_CLEAR
MOV DI,STARTING_PAGE
MOV STARTING_LINE,DI
MOV AL,0 ; clear whole window
JMP SHORT SCROLL_SCREEN
NOT_CLEAR:
; ----------
CMP AL,0Dh ; carriage return?
JNE NOT_CR
NEW_LINE:
MOV DI,STARTING_LINE
ADD DI,160
JMP SHORT TEST_RIGHT
NOT_CR:
; ----------
CMP AL,1Ch ; move right?
JNE NOT_RIGHT
ADD DI,2
JMP SHORT TEST_RIGHT
NOT_RIGHT:
; ----------
CMP AL,1Dh ; move left?
JNE NOT_LEFT
SUB DI,2
JMP SHORT TEST_LEFT
NOT_LEFT:
; ----------
CMP AL,1Eh ; move up?
JNE NOT_UP
SUB DI,160
JMP SHORT TEST_LEFT
NOT_UP:
; ----------
CMP AL,1Fh ; move down?
JNE NOT_DOWN
ADD DI,160
JMP SHORT TEST_RIGHT
NOT_DOWN:
JMP SHORT TEST_FOR_TAB
; ----------
TEST_RIGHT:
MOV AX,DI ; are we past line 24?
SUB AX,STARTING_PAGE
CMP AX,3840
JL VALID_RIGHT
MOV DI,STARTING_PAGE ; back at start of last line
ADD DI,3680
MOV STARTING_LINE,DI
MOV AL,1 ; scroll one line
JMP SHORT SCROLL_SCREEN
VALID_RIGHT:
SUB AL,AL
RET
; ----------
SCROLL_SCREEN:
PUSH CX
MOV CX,0 ; start in upper left corner
PUSH DX
MOV DX,174Fh ; end in lower right, one line up
MOV AH,6
PUSHF ; simulate an INT 10
CALL DWORD PTR ORIG_INT10
POP DX
POP CX
SUB AL,AL
RET
; ----------
TEST_LEFT:
CMP DI,STARTING_PAGE
JGE VALID_LEFT
MOV DI,STARTING_PAGE
VALID_LEFT:
SUB AL,AL
RET
; ----------
TEST_FOR_TAB:
CMP AL,09h ; tab?
JNE NOT_TAB
PUSH CX
PUSH DX
MOV AX,DI
SUB AX,STARTING_LINE
SHR AX,1 ; discount attribute bytes
SUB DX,DX
MOV CX,8
DIV CX
MOV CX,8 ; tab positions are every 8 charactes
SUB CX,DX ; subtract off remainder
MOV BL,' ' ; write some blanks
POP DX
TAB_LOOP:
CALL DISPLAY_CHAR
LOOP TAB_LOOP
POP CX
SUB AL,AL
RET
NOT_TAB:
; ----------
CMP AL,08h ; backspace?
JNE NOT_BACKSPACE
CMP DI,STARTING_LINE
JE AT_START
SUB DI,2 ; back up a space
AT_START:
MOV BL,' ' ; write a blank
CALL DISPLAY_CHAR
SUB DI,2 ; back up a space
SUB AL,AL
RET
NOT_BACKSPACE:
; ----------
MOV AH,2
SUB AH,1 ; set flag to display the char
RET
SPECIAL ENDP
;______________________________________________________________________________
; routine to display a character in BL - used only for special characters
; attribute is in BH
DISPLAY_CHAR PROC NEAR
CLI
HSYNC_WAIT3:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JNZ HSYNC_WAIT3 ; wait for retrace
HSYNC_WAIT4:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JZ HSYNC_WAIT4 ; wait for retrace
MOV AX,BX
STOSW ; store character and attribute
STI
RET
DISPLAY_CHAR ENDP
;______________________________________________________________________________
LASTADDR LABEL BYTE
COPYRIGHT LABEL BYTE
DB 10,13
DB ' CLUBware (tm)',10,13,10,13
DB 'BASPRINT - Enhancement to the Basic Interpreter Print statement'
DB 10,13,10,13
DB ' Copyright 1984 Rayhawk Automation N.W. Inc',10,13
DB ' P.O. Box 1427',10,13
DB ' Beaverton, Oregon 97075',10,13,'$'
;______________________________________________________________________________
BASPRINT PROC FAR
PUSH DS ; Push addr of Program Segment Prefix
SUB AX,AX ; Zero AX
PUSH AX ; Push zero onto stack
; (offset of INT 20 within PSP)
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | take over the INT 10h |
; | interrupt if not already done |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV DS,AX ; address low memory
LDS BX,DWORD PTR DS:[02D0h] ; load interrupt vector for int B4
MOV AX,WORD PTR PRINTER
CMP AX,WORD PTR DS:[BX]
JNE NOT_HERE_YET
MOV AX,WORD PTR PRINTER+2
CMP AX,WORD PTR DS:[BX+2]
JNE NOT_HERE_YET
MOV AX,WORD PTR PRINTER+4
CMP AX,WORD PTR DS:[BX+4]
JNE NOT_HERE_YET
MOV AX,WORD PTR PRINTER+6
CMP AX,WORD PTR DS:[BX+6]
JNE NOT_HERE_YET
JMP ALREADY_RESIDENT
NOT_HERE_YET:
SUB AX,AX
MOV DS,AX
MOV AX,WORD PTR DS:[40h] ; save original int10
MOV ORIG_INT10,AX
MOV AX,WORD PTR DS:[42h]
MOV ORIG_INT10+2,AX
MOV AX,SEG BASPRINT
MOV DS,AX
MOV DX,OFFSET INTSWAP ; Load offset of interrupt service mod
MOV AX,2510h ; Prepare for DOS service call type 25
; to establish service for INT 10
INT 21h ; Ask DOS to establish service
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | issue copyright message |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV DX,OFFSET COPYRIGHT
MOV AH,9
INT 21h
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | modify INT 20 into INT 27 in the |
; | program segment prefix |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV BYTE PTR ES:[01],27h ; Change INT 20h to INT 27h
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | 6) load address of ending tag into DX |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV AX,SEG LASTADDR
SUB AX,SEG BASPRINT
MOV CL,4 ; prepare for 4 bit shift
SHL AX,CL ; shift up (convert from seg to abs)
ADD AX,OFFSET LASTADDR ; add address of bottom location
ADD AX,0103h ; Pad offset because DOS measures
; offset relative to Program
; Segment Prefix
MOV DX,AX ; leave where DOS will find it
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | 7) use RET FAR to return to DOS and |
; | leave service routine resident |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
ALREADY_RESIDENT:
RET
BASPRINT ENDP
SCRDSEG ENDS
;______________________________________________________________________________
STACK SEGMENT PARA STACK 'STACK'
DB 24 DUP('STACK***')
TOPSTACK DB 0
STACK ENDS
END BASPRINT